#!/usr/bin/env bash
#
# Run the include-what-you-use tool (iwyu) on a file in the webrtc source
# directory.
#
# The script uses a subsequent grep pass to remove #include files
# that are problematic to include.
#
# In order to handle include paths correctly, you need to provide
# a compile DB (aka compile_commands.json).
# You can create it in one of the following ways:
# "gn gen --export-compile-commands path/to/out"
# "tools/clang/scripts/generate_compdb.py -p path/to/out > compile_commands.json"
# If "out/Default" exists, the script will attempt to generate it for you.
#
# To get iwyu on Debian/glinux, do "sudo apt-get install iwyu".

# Debug level, also controlled by the "-d" argument.
# Set this to 1 to get more debug information.
# Set this to 2 to also get a dump of the iwyu tool output.
DEBUG=0

set -e
if [ $DEBUG -gt 0 ]; then
  set -x
fi

error() {
  echo "$*" >&2
  exit 1
}

find_alternates() {
  for name in "$@"
  do
    name_path=$(which "${name}")
    if [ ! -z "${name_path}" ]; then
      echo ${name_path}
      return 0
    fi
  done
  error "Could not find any of the tools '$@' in PATH."
  return 1
}

IWYU_TOOL=$(find_alternates iwyu_tool iwyu_tool.py)
FIX_INCLUDE=$(find_alternates fix_include fix_includes.py)
FIX_INCLUDE_ARGS=''
IWYU_TOOL_DIR="${IWYU_TOOL_DIR:-tools_webrtc/iwyu}"
COMPILE_COMMANDS=''

usage() {
  echo "Usage: $0 [ -c compile-commands-file.json ] [-r] file.cc"
  echo "Runs the IWYU and fix-include on a CC file and its associated .h file"
  echo "Arguments:"
  echo " -c compile-commands: Compiler command file"
  echo " -r : Remove non-required includes from .h file"
  echo " -d <n> : Set debug level to <n>"
  echo " -h : Print this help message"
  echo "(default command file: out/Default/compile_commands.json - this"
  echo "will be generated if not present"
}

while getopts 'c:d:rh' opts; do
  case "${opts}" in
    c) COMPILE_COMMANDS="${OPTARG}" ;;
    r) FIX_INCLUDE_ARGS="${FIX_INCLUDE_ARGS} --nosafe_headers" ;;
    d) DEBUG=${OPTARG};if [ $DEBUG -gt 0 ]; then set -x; fi  ;;
    h) usage; exit 1 ;;
    *) error "Unexpected option ${opts}" ;;
  esac
done
shift $(expr $OPTIND - 1 )

if [[ -z "$COMPILE_COMMANDS" ]]; then
  if [ -d "out/Default" ]; then
    if [ ! -f "out/Default/compile_commands.json" ]; then
      gn gen --export-compile-commands out/Default
    fi
    COMPILE_COMMANDS="out/Default/compile_commands.json"
  else
    error "compile_commands.json must be passed."
  fi
fi

FILE="$1"

if [ ! -f $FILE_CC ]; then
  error "File $FILE is not found"
fi

# Find the .h file that IWYU will modify, if any.
FILE_CC=$FILE
if [ -f $(dirname $FILE)/$(basename -s .cc $FILE).h ]; then
  FILE_H=$(dirname $FILE)/$(basename -s .cc $FILE).h
else
  FILE_H=""
fi

tmpfile=$(realpath $(mktemp iwyu.XXXXXXX))
trap 'rm -f -- "${tmpfile}"' EXIT

# IWYU has a confusing set of exit codes. Discard it.
"$IWYU_TOOL" -p "$COMPILE_COMMANDS" "$FILE_CC" -- -Xiwyu --no_fwd_decls \
  -Xiwyu --mapping_file=../../$IWYU_TOOL_DIR/mappings.imp \
  >& ${tmpfile} || echo "IWYU done, code $?"

if grep 'fatal error' ${tmpfile}; then
  echo "iwyu run failed"
  cat ${tmpfile}
  exit 1
else
  if [ $DEBUG -gt 1 ]; then
    cat ${tmpfile}
  fi
  # In compile_commands.json, the file name is recorded
  # as a relative path to the build directory.
  pushd "$(dirname "$COMPILE_COMMANDS")" || error "pushd failed"
  "$FIX_INCLUDE" $FIX_INCLUDE_ARGS < ${tmpfile} || echo "Some files modified"
  popd
fi

grep -v -f tools_webrtc/iwyu/iwyu-filter-list $FILE_CC > $FILE_CC.new
mv $FILE_CC.new $FILE_CC

if [ -n "$FILE_H" ]; then
  grep -v -f tools_webrtc/iwyu/iwyu-filter-list $FILE_H > $FILE_H.new
  mv $FILE_H.new $FILE_H
fi

echo "Finished. Check diff, compile and git cl format before uploading."
